/*
 * 本控制器演示如何查询内容项，这次以新建的 Person 内容类型为例。
 * 需要注意的是，ContentItems 存储在文档中（包含序列化的部件和字段），
 * 并且可以有多个索引记录引用同一个内容项（如 PersonPartIndex）。
 *
 * 还将演示如何通过代码修改和创建内容项。
 *
 * 注意：没有为 Person 显示编辑器的自定义控制器或操作，
 * 请在管理后台创建一些 Person 内容项（包括年龄大于 30 岁的）。
 */

using Ruitu.Edu.TrainingDemo.Indexes;
using Ruitu.Edu.TrainingDemo.Models;
using Microsoft.AspNetCore.Mvc;
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Display;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.Modules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using YesSql;

namespace Ruitu.Edu.TrainingDemo.Controllers;

/// <summary>
/// 人员列表控制器 - 演示内容项的查询、修改和创建
/// 
/// 主要用途：
/// - 查询特定条件的内容项
/// - 通过代码修改和创建内容项
/// - 展示内容项与索引的结合用法
/// 
/// 使用场景：
/// - 业务数据的批量查询与展示
/// - 通过代码批量修改内容项
/// - 动态创建内容项
/// 
/// 设计原因：
/// - 利用 Orchard Core 内容管理 API
/// - 展示内容项生命周期管理
/// 
/// 设计模式：
/// - 依赖注入模式：通过构造函数注入内容管理服务
/// - 索引查询模式：通过索引高效筛选内容项
/// </summary>
public sealed class PersonListController : Controller
{
    private readonly ISession _session;
    private readonly IClock _clock;
    private readonly IContentItemDisplayManager _contentItemDisplayManager;
    private readonly IUpdateModelAccessor _updateModelAccessor;
    private readonly IContentManager _contentManager;

    /// <summary>
    /// 构造函数 - 注入内容管理相关服务
    /// </summary>
    public PersonListController(
        ISession session,
        IClock clock,
        IContentItemDisplayManager contentItemDisplayManager,
        IUpdateModelAccessor updateModelAccessor,
        IContentManager contentManager)
    {
        _session = session;
        _clock = clock;
        _contentItemDisplayManager = contentItemDisplayManager;
        _updateModelAccessor = updateModelAccessor;
        _contentManager = contentManager;
    }

    /// <summary>
    /// 查询年龄大于 30 岁的人员并展示
    /// 访问路径：/Ruitu.Edu.TrainingDemo/PersonList/OlderThan30
    /// </summary>
    public async Task<IActionResult> OlderThan30()
    {
        var thresholdDate = _clock.UtcNow.AddYears(-30);
        var people = await _session
            .Query<ContentItem, PersonPartIndex>(index => index.BirthDateUtc < thresholdDate)
            .ListAsync();
        // 构建内容项的显示 shape。
        // 注意：IContentItemDisplayManager 专为内容项设计，会自动处理所有部件和字段的驱动。
        // 下一站：Drivers/PersonPartDisplayDriver
        var shapes = await people.AwaitEachAsync(async person =>
        {
            await _contentManager.LoadAsync(person);
            return await _contentItemDisplayManager.BuildDisplayAsync(person, _updateModelAccessor.ModelUpdater, "Summary");
        });
        // 可在 Views/PersonList/OlderThan30.cshtml 查看效果
        return View(shapes);
    }

    /// <summary>
    /// “返老还童”操作 - 批量将 90 岁以上人员变年轻
    /// 访问路径：/Ruitu.Edu.TrainingDemo/PersonList/FountainOfEternalYouth
    /// </summary>
    public async Task<string> FountainOfEternalYouth()
    {
        // 查询 90 岁以上的人员内容项。
        var thresholdDate = _clock.UtcNow.AddYears(-90);
        var oldPeople = (await _session
            .Query<ContentItem, PersonPartIndex>(index => index.BirthDateUtc < thresholdDate)
            .ListAsync())
            .ToList();
        foreach (var person in oldPeople)
        {
            await _contentManager.LoadAsync(person);
            var eighteenYearOld = _clock.UtcNow.AddYears(-18);
            // 正确修改内容项部件属性，避免只改索引。
            person.Alter<PersonPart>(part =>
            {
                part.BirthDateUtc = eighteenYearOld;
                part.Biography.Text += " I'm young again!";
            });
            // 保存并发布内容项。
            await _contentManager.UpdateAsync(person);
            await _contentManager.PublishAsync(person);
        }
        // 返回操作结果。
        return "People modified: " +
            (oldPeople.Count != 0 ?
                string.Join(", ", oldPeople.Select(person => person.As<PersonPart>().Name)) :
                "Nobody. Did you create people older than 90?");
    }

    /// <summary>
    /// 创建“仿生人”内容项
    /// 访问路径：/Ruitu.Edu.TrainingDemo/PersonList/CreateAnAndroid
    /// </summary>
    public async Task<string> CreateAnAndroid()
    {
        // 新建内容项对象。
        var person = await _contentManager.NewAsync(ContentTypes.PersonPage);
        // 生成唯一姓名。
        var serialNumber = _clock.UtcNow.Ticks;
        var name = $"X Doe #{serialNumber.ToTechnicalString()}";
        // 设置部件属性。
        person.Alter<PersonPart>(part =>
        {
            part.Name = name;
            part.BirthDateUtc = _clock.UtcNow;
            part.Handedness = Handedness.Right;
        });
        // 正确设置内容字段。
        var personPart = person.As<PersonPart>();
        personPart.Alter<TextField>(nameof(PersonPart.Biography), field => field.Text = "I'm sentient now!");
        // 保存并发布内容项。
        await _contentManager.CreateAsync(person, VersionOptions.Draft);
        await _contentManager.UpdateAsync(person);
        await _contentManager.PublishAsync(person);
        // 返回创建结果。
        return $"{name} came into existence.";
    }
}
